import {
  checkSGSToken,
  getOrderId,
  getOrderDetail,
  getSampleDataByOrderId,
  saveOrderDetail,
  saveCurrentLab,
  getEnquiryId,
  getEnquiryDetail,
  getSampleDataByEnquiryId,
  saveEnquiryDetail,
  queryCustomer,
} from "../models/gpo-api.js"
import { z } from 'zod'
import { OpenAPIRegistry, extendZodWithOpenApi } from '@asteasolutions/zod-to-openapi'

extendZodWithOpenApi(z)

export const registryData = new OpenAPIRegistry()

registryData.registerComponent(
  'securitySchemes',
  'bearerAuth',
  {
    type: 'http',
    scheme: 'bearer',
    bearerFormat: 'JWT'
  }
)

const updateOrderDetailSchema = z.object({
  gpoNo: z.string().openapi({ description: 'The order/enquiry number.', example: 'SZHL2409000577TY' }),
  type: z.enum(['order', 'enquiry']).openapi({ description: 'The type of the order/enquiry.', example: 'order' }),
  labCode: z.string().openapi({ description: 'The lab code.', example: 'SZ HL' }),
  sgsToken: z.string().openapi({ description: 'The SGS token for authentication.', example: 'c38bfe1840054e0189f9ee92001258a6' }),
  data: z.object({
    DFFForm: z.object({}).optional().openapi({ description: 'The object containing product update information.' }),
    DFFGrid: z.array(z.object({})).optional().openapi({ description: 'The object containing product sample update information.' }),
    Applicant: z.object({}).optional().openapi({ description: 'The object containing applicant information.' }),
    Payer: z.object({}).optional().openapi({ description: 'The object containing payer information.' }),
    Buyer: z.object({}).optional().openapi({ description: 'The object containing buyer information.' }),
    Manufacture: z.object({}).optional().openapi({ description: 'The object containing manufacture information.' }),
    Supplier: z.object({}).optional().openapi({ description: 'The object containing supplier information.' }),
    ReportInfo: z.object({}).optional().openapi({ description: 'The object containing report info and service requirement.' }),
    Header: z.object({}).optional().openapi({ description: 'The object containing header information.' })
  }).openapi({ description: 'The data to update.' })
}).openapi({ description: 'Update order/enquiry details' })

const updateOrderDetailResponseSchema = z.object({
  success: z.boolean().openapi({ description: 'Indicates if the update was successful.', example: true }),
  data: z.object({}).openapi({ description: 'The result of saving details.' })
}).openapi({ description: 'The order/enquiry details were successfully updated.' })

registryData.registerPath({
  method: 'post',
  path: '/data/updateGPODetail',
  description: 'Update order/enquiry details',
  summary: 'Update order/enquiry details',
  security: [{ bearerAuth: [] }],
  request: {
    body: {
      description: 'The order/enquiry details to update.',
      content: {
        'application/json': {
          schema: updateOrderDetailSchema
        }
      }
    }
  },
  responses: {
    200: {
      description: 'The order/enquiry details were successfully updated.',
      content: {
        'application/json': {
          schema: updateOrderDetailResponseSchema
        }
      }
    }
  }
})

export const updateGPODetail = async (ctx) => {
  const { type } = ctx.request.body
  switch (type) {
    case 'order':
      await updateOrderDetail(ctx)
      break
    case 'enquiry':
      await updateEnquiryDetail(ctx)
      break
  }
}

const updateOrderDetail = async (ctx) => {
  const { gpoNo, sgsToken, data = {} } = ctx.request.body
  const {
    DFFForm = {},
    DFFGrid = [{}],
    Applicant = {},
    Payer = {},
    Buyer = {},
    Manufacture = {},
    Supplier = {},
    ReportInfo = {},
    Header = {}
  } = data
  const orderId = await getOrderId(gpoNo)
  const orderDetail = await getOrderDetail(orderId)

  const body = orderDetail
  body.sgsToken = sgsToken

  if (Array.isArray(body.headers.tagValueSelectDTOS)) {
    body.headers.tagValueJson = JSON.stringify(body.headers.tagValueSelectDTOS.filter(e => e.selectedValue).map(e => {
      e.tagValues = undefined
      return e
    }))
    body.headers.tagValueSelectDTOS = []
  } else {
    body.headers.tagValueJson = JSON.stringify([])
  }

  const sampleData = await getSampleDataByOrderId(orderId)
  body.product = sampleData.productSampleRspList[0].product
  body.productSamples = sampleData.productSampleRspList[0].productSamples
  body.version.oldProductVersionId = sampleData.oldProductVersionId
  body.version.oldProductSampleVersionId = sampleData.oldProductSampleVersionId

  body.product = Object.assign({}, body.product, DFFForm)
  DFFGrid.forEach((item, index) => {
    body.productSamples[index] = Object.assign({}, body.productSamples[index], item)
  })
  body.applicant = Object.assign({}, body.applicant, Applicant)
  body.payer = Object.assign({}, body.payer, Payer)
  body.buyer = Object.assign({}, body.buyer, Buyer)
  body.manufacture = Object.assign({}, body.manufacture, Manufacture)
  body.supplier = Object.assign({}, body.supplier, Supplier)
  body.testRequest = Object.assign({}, body.testRequest, ReportInfo)
  body.headers = Object.assign({}, body.headers, Header)

  // convert some fields to string
  body.headers.needConclusion = body.headers?.needConclusion?.toString()
  body.headers.sealFlag = body.headers?.sealFlag?.toString()

  ctx.body = await saveOrderDetail(body)
}

const updateEnquiryDetail = async (ctx) => {
  const { gpoNo, sgsToken, labCode, data = {} } = ctx.request.body
  const {
    DFFForm = {},
    DFFGrid = [{}],
    Applicant = {},
    Payer = {},
    Buyer = {},
    Manufacture = {},
    Supplier = {},
    ReportInfo = {},
    Header = {}
  } = data
  await saveCurrentLab(labCode, sgsToken)
  const enquiryId = await getEnquiryId(gpoNo)
  const enquiryDetail = await getEnquiryDetail(enquiryId, sgsToken)

  const body = enquiryDetail
  body.sgsToken = sgsToken

  if (Array.isArray(body.headers.tagValueSelectDTOS)) {
    body.headers.tagValueJson = JSON.stringify(body.headers.tagValueSelectDTOS.filter(e => e.selectedValue).map(e => {
      e.tagValues = undefined
      return e
    }))
    body.headers.tagValueSelectDTOS = []
  } else {
    body.headers.tagValueJson = JSON.stringify([])
  }

  const sampleData = await getSampleDataByEnquiryId(enquiryId)
  body.productSampleRspList = sampleData.productSampleRspList

  body.productSampleRspList[0].product = Object.assign({}, body.productSampleRspList[0].product, DFFForm)
  DFFGrid.forEach((item, index) => {
    body.productSampleRspList[0].productSamples[index] = Object.assign({}, body.productSampleRspList[0].productSamples[index], item)
  })
  body.applicant = Object.assign({}, body.applicant, Applicant)
  body.payer = Object.assign({}, body.payer, Payer)
  body.buyer = Object.assign({}, body.buyer, Buyer)
  body.manufacture = Object.assign({}, body.manufacture, Manufacture)
  body.supplier = Object.assign({}, body.supplier, Supplier)
  body.testRequest = Object.assign({}, body.testRequest, ReportInfo)
  body.headers = Object.assign({}, body.headers, Header)

  // convert some fields to string
  body.headers.needConclusion = body.headers?.needConclusion?.toString()
  body.headers.sealFlag = body.headers?.sealFlag?.toString()

  ctx.body = await saveEnquiryDetail(body, sgsToken)
}


const getCustomerSchema = z.object({
  buCode: z.string().openapi({ description: 'The business unit code.', example: 'HL' }),
  locationCode: z.string().openapi({ description: 'The location code.', example: 'SZ' }),
  customerNameCn: z.string().optional().openapi({ description: 'The customer name used for querying.', example: 'target' }),
  customerGroupName: z.string().optional().openapi({ description: 'The customer group name used for querying.', example: 'target' }),
  displayType: z.string().optional().openapi({ description: 'The display type, set account for buyer.', example: 'account' }),
  code: z.string().optional().openapi({ description: 'The customer code indicating the query type (e.g., SHIP_TO, BILL_TO). using for applicant and payer', example: 'SHIP_TO' }),
  contactRole: z.string().optional().openapi({ description: 'The contact role corresponding to the code. same as code.', example: 'SHIP_TO' })
}).openapi({ summary: 'Retrieve a list of customers', description: 'Fetches a list of customers based on various filters provided in the form data.' })

const getCustomerResponseSchema = z.object({
  success: z.boolean().openapi({ description: 'Indicates if the retrieval was successful.', example: true }),
  data: z.array(z.object({})).openapi({ description: 'A list of customers.' })
}).openapi({ description: 'A list of customers was successfully retrieved.' })

registryData.registerPath({
  method: 'post',
  path: '/data/getCustomers',
  description: 'Fetches a list of customers based on various filters provided in the form data.',
  summary: 'Retrieve a list of customers',
  security: [{ bearerAuth: [] }],
  request: {
    body: {
      description: 'The filters used to retrieve customers.',
      content: {
        'application/json': {
          schema: getCustomerSchema
        }
      }
    }
  },
  responses: {
    200: {
      description: 'A list of customers was successfully retrieved.',
      content: {
        'application/json': {
          schema: getCustomerResponseSchema
        }
      }
    }
  }
})

export const getCustomers = async (ctx) => {
  const {
    buCode,
    locationCode,
    customerNameCn,
    customerGroupName,
    displayType,
    code,
    contactRole
  } = ctx.request.body
  const result = await queryCustomer({
    buCode,
    locationCode,
    customerNameCn,
    customerGroupName,
    displayType,
    code,
    contactRole
  })
  if (Array.isArray(result)) {
    ctx.body = {
      success: true,
      data: result
    }
  } else {
    ctx.body = {
      success: false
    }
  }
}


const getUserInfoSchema = z.object({
  sgsToken: z.string().openapi({ description: 'The SGS token.', example: 'c38bfe1840054e0189f9ee92001258a6' })
}).openapi({ summary: 'Get User Info', description: 'Get user info by SGS token' })

const getUserInfoResponseSchema = z.object({
  success: z.boolean().openapi({ description: 'Indicates if the verification was successful.', example: true }),
  userInfo: z.object({}).openapi({ description: 'The information of the user associated with the SGS token.' })
}).openapi({ description: 'Token verification successful, returns user info.' })

registryData.registerPath({
  method: 'post',
  path: '/auth/getUserInfo',
  description: 'Get user info by SGS token',
  sunmmary: 'Get User Info',
  request: {
    body: {
      description: 'The SGS token.',
      content: {
        'application/json': {
          schema: getUserInfoSchema
        }
      }
    }
  },
  responses: {
    200: {
      description: 'Token verification successful, returns user info.',
      content: {
        'application/json': {
          schema: getUserInfoResponseSchema
        }
      }
    },
    400: {
      description: 'Invalid SGS token.'
    }
  }
})

registryData.registerPath({
  method: 'post',
  path: '/data/checkToken',
  description: 'Verifies the provided SGS token and retrieves associated user info.',
  summary: 'Check SGS token',
  security: [{ bearerAuth: [] }],
  request: {
    body: {
      required: true,
      content: {
        'application/json': {
          schema: getUserInfoSchema
        }
      }
    }
  },
  responses: {
    200: {
      description: 'Token verification successful, returns user info.',
      content: {
        'application/json': {
          schema: getUserInfoResponseSchema
        }
      }
    },
    400: {
      description: 'Invalid SGS token.'
    }
  }
})

export const checkToken = async (ctx) => {
  let { sgsToken } = ctx.request.body
  let { userInfo } = await checkSGSToken(sgsToken)
  if (userInfo) {
    // return user info if the token is valid
    ctx.body = {
      success: true,
      userInfo
    }
  } else {
    // return 400 if the token is invalid
    ctx.status = 400
    ctx.body = {
      success: false,
      userInfo: {}
    }
  }
}